define [
  'handlebars'
  'chaplin'
  'lib/utils' # Just load the view helpers, no return value
  'lib/enums'
  'lib/handlebars_helper' # Just load the view helpers, no return value
], (Handlebars, Chaplin, utils, Enums) ->

  'use strict'

  class View extends Chaplin.View

    # Override Attributes
    # =============================================================

    container         : '#module-container'
    actionElsSelector : 'button, a, .btn, select, input, i, textarea'

    # Override Methods
    # =============================================================

    constructor: ->
      # Wrap `initialize` so `afterInitialize` is called afterwards
      # Only wrap if there is an overring method, otherwise we
      # can call the `after-` method directly
      unless @initialize is View::initialize
        @wrapMethod 'initialize'

      # Wrap `render` so `afterRender` is called afterwards
      unless @render is View::render
        @wrapMethod 'render'

      super

    initialize: (options={}) ->
      super

      _(@).extend options

      # Call `afterInitialize` if `initialize` was not wrapped
      @afterInitialize() unless @initializeIsWrapped


    render: ->
      @publishEvent 'load:hide'
      super
      @afterRender() unless @renderIsWrapped

    getTemplateFunction: ->
      # Template compilation
      # --------------------

      # This demo uses Handlebars templates to render views.
      # The template is loaded with Require.JS and stored as string on
      # the view prototype. On rendering, it is compiled on the
      # client-side. The compiled template function replaces the string
      # on the view prototype.
      #
      # In the end you might want to precompile the templates to JavaScript
      # functions on the server-side and just load the JavaScript code.
      # Several precompilers create a global JST hash which stores the
      # template functions. You can get the function by the template name:
      #
      # templateFunc = JST[@templateName]

      template = @template

      if typeof template is 'string'
        # Compile the template string to a function and save it
        # on the prototype. This is a workaround since an instance
        # shouldn’t change its prototype normally.
        templateFunc = Handlebars.compile template
        @constructor::template = templateFunc
      else
        templateFunc = template

      templateFunc

    # Additional Methods
    # =============================================================

    # Method wrapping to enable `afterRender` and `afterInitialize`
    # -------------------------------------------------------------

    # Wrap a method in order to call the corresponding
    # `after-` method automatically
    # ------------------

    wrapMethod: (name) ->
      instance = this
      # Enclose the original function
      func = instance[name]
      # Set a flag
      instance["#{name}IsWrapped"] = true
      # Create the wrapper method
      instance[name] = ->
        # Stop if the view was already disposed
        return false if @disposed
        # Call the original method
        func.apply instance, arguments
        # Call the corresponding `after-` method
        instance["after#{utils.upcase(name)}"] arguments...
        # Return the view
        instance

    afterInitialize: ->

    afterRender: ->
      do @cacheDOMElements
      do @instanceSubviews
      do @registryDOMEvents
      do @setupTooltip

    getError: (resp) ->
      json = $.parseJSON resp.responseText

      return {
        message : json.erro.message
        code    : json.erro.code
        status  : json.status
      }

    # Bind to a model event
    # ------------------

    modelBind: (type, handler) ->
      if typeof type isnt 'string'
        throw new TypeError 'View#modelBind: ' +
          'type must be a string'
      if typeof handler isnt 'function'
        throw new TypeError 'View#modelBind: ' +
          'handler argument must be function'

      # Get model/collection reference
      modelOrCollection = @model or @collection
      unless modelOrCollection
        throw new TypeError 'View#modelBind: no model or collection set'

      # Ensure that a handler isn’t registered twice
      modelOrCollection.off type, handler, this

      # Register model handler, force context to the view
      modelOrCollection.on type, handler, this

    # Unbind from a model event
    # ------------------

    modelUnbind: (type, handler) ->
      if typeof type isnt 'string'
        throw new TypeError 'View#modelUnbind: ' +
          'type argument must be a string'
      if typeof handler isnt 'function'
        throw new TypeError 'View#modelUnbind: ' +
          'handler argument must be a function'

      # Get model/collection reference
      modelOrCollection = @model or @collection
      return unless modelOrCollection

      # Remove model handler
      modelOrCollection.off type, handler

    # Unbind all recorded model event handlers
    # ------------------

    modelUnbindAll: () ->
      # Get model/collection reference
      modelOrCollection = @model or @collection
      return unless modelOrCollection

      # Remove all handlers with a context of this view
      modelOrCollection.off null, null, this

    # Setup a simple one-way model-view binding
    # Pass changed attribute values to specific elements in the view
    # For form controls, the value is changed, otherwise the element
    # text content is set to the model attribute value.
    # Example: @pass 'attribute', '.selector'
    # ------------------

    pass: (attribute, selector) ->
      @modelBind "change:#{attribute}", (model, value) =>
        $el = @$(selector)
        if $el.is('input, textarea, select, button')
          $el.val value
        else
          $el.text value

    # Instancia as subviews utilizadas
    instanceSubviews: ->

    # Faz o cache dos elementos do DOM que são manipulados durante a execução
    cacheDOMElements: ->
      @DOMElements = {}
      @addedToDOM = true
      return @DOMElements

    # Registra os eventos dos elementos do DOM
    registryDOMEvents: ->

    # Loader na tela
    createLoader: ->
    hideLoader: ->

    disableActions: (label) ->
      # @changeSubmitLabel label

      $el = @$form or @$el
      if $el then $el.find(@actionElsSelector).attr('disabled', true).addClass('disabled')

    enableActions: (label)->
      # @changeSubmitLabel label
      $el = @$form or @$el
      if $el then $el.find(@actionElsSelector).removeAttr('disabled').removeClass('disabled')

    setupTooltip: ->
      $elements = @$('[title]')
      if $elements.length
        $elements.each (i, el) =>
          $el = $ el

          title         = $el.attr 'title'
          titleFiltered = Enums.messages[title] or title

          $el.attr 'title', titleFiltered

        $elements.tooltip 'destroy'
        $elements.tooltip()

    closeTooltips: ->
      do $('.tooltip').remove
